#include "string_util.hh"
#include "os_util.hh"

#include <regex>
#include <fstream>

#if defined(_MSC_VER)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <Dbghelp.h>
#pragma comment(lib, "DbgHelp")
#elif defined(__GNUC__)
#include <cxxabi.h>
#endif

#include "../../thirdparty/jsoncpp/include/json/json.h"

namespace kratos {
namespace util {

void splitOf(const std::string &src, const std::string &delim,
             std::vector<std::string> &result) {
  result.clear();
  if (delim.size() >= src.size()) {
    return;
  }
  if (src.empty() || delim.empty()) {
    return;
  }
  size_t end = 0;
  size_t begin = 0;
  while (end != std::string::npos) {
    end = src.find_first_of(delim, begin);
    if (end != std::string::npos) {
      result.push_back(src.substr(begin, end - begin));
      begin = end + 1;
    } else {
      result.push_back(src.substr(begin, std::string::npos));
    }
  }
}

void split(const std::string &src, const std::string &delim,
           std::vector<std::string> &result) {
  result.clear();
  if (src.empty() || delim.empty()) {
    return;
  }
  //if (delim.size() >= src.size()) {
  //  return;
  //}
  size_t end = 0;
  size_t begin = 0;
  std::string substr;
  while (end != std::string::npos) {
    end = src.find(delim, begin);
    if (end != std::string::npos) {
      substr = src.substr(begin, end - begin);
    } else {
      substr = src.substr(begin, std::string::npos);
    }
    if (!substr.empty() && (substr != delim)) {
      result.push_back(substr);
    }
    begin = end + delim.size();
  }
}

void splitBy(const std::string &src, char delim,
             std::vector<std::string> &result) {
  result.clear();
  if (src.empty()) {
    return;
  }
  std::string item;
  for (auto &c : src) {
    if (c != delim) {
      item.push_back(c);
    } else {
      item.push_back(delim);
      result.push_back(std::move(item));
    }
  }
  if (!item.empty()) {
    result.push_back(std::move(item));
  }
}

bool endWith(const std::string &src, const std::string &s) {
  if (s.size() >= src.size()) {
    return false;
  }
  if (src.empty() || s.empty()) {
    return false;
  }
  return src.compare(src.size() - s.size(), s.size(), s) == 0;
}

bool startWith(const std::string &src, const std::string &s) {
  if (s.size() >= src.size()) {
    return false;
  }
  if (src.empty() || s.empty()) {
    return false;
  }
  return src.compare(0, s.size(), s) == 0;
}

std::string remove(const std::string &src, const std::string &s) {
  if (s.size() >= src.size()) {
    return src;
  }
  if (s.empty() || src.empty()) {
    return src;
  }
  auto target = src;
  std::size_t pos = 0;
  while (pos != std::string::npos) {
    pos = target.find(s, pos);
    if (std::string::npos != pos) {
      target.erase(pos, s.size());
    } else {
      break;
    }
  }
  return target;
}

std::string trim(const std::string &src, const std::string &delim) {
  if (src.empty()) {
    return src;
  }
  std::string s(src);
  s.erase(0, s.find_first_not_of(delim));
  s.erase(s.find_last_not_of(delim) + 1);
  return s;
}

std::string ltrim(const std::string &src, const std::string &delim) {
  if (src.empty()) {
    return src;
  }
  std::string s(src);
  s.erase(0, s.find_first_not_of(delim));
  return s;
}

std::string rtrim(const std::string &src, const std::string &delim) {
  if (src.empty()) {
    return src;
  }
  std::string s(src);
  s.erase(s.find_last_not_of(delim) + 1);
  return s;
}

std::string replace(const std::string &src, const std::string &pattern,
                    const std::string &dest) {
  if (pattern.size() > src.size()) {
    return src;
  }
  std::string::size_type pos = 0;
  auto srclen = pattern.size();
  auto dstlen = dest.size();
  std::string s(src);
  pos = s.find(pattern, pos);
  while (std::string::npos != pos) {
    s.replace(pos, srclen, dest);
    pos += dstlen;
    pos = s.find(pattern, pos);
  }
  return s;
}

std::string get_file_name(const std::string &src) {
  if (src.empty()) {
    return src;
  }
  std::vector<std::string> result;
  std::string last;
  split(src, "/", result);
  if (result.size() == 1) {
    split(src, "\\", result);
    if (result.size() == 1) {
      last = src;
    } else {
      last = result.back();
    }
  } else {
    last = result.back();
  }
  result.clear();
  split(last, ".", result);
  if (result.size() == 1) {
    return last;
  }
  return result.front();
}

std::string get_file_full_name(const std::string &src) {
  if (src.empty()) {
    return src;
  }
  std::vector<std::string> result;
  std::string last;
  split(src, "/", result);
  if (result.size() == 1) {
    split(src, "\\", result);
    if (result.size() == 1) {
      last = src;
    } else {
      last = result.back();
    }
  } else {
    last = result.back();
  }
  return last;
}

std::string get_path(const std::string &src) {
  if (src.empty()) {
    return src;
  }
  auto pos = src.find_last_of('/');
  if (std::string::npos == pos) {
    pos = src.find_last_of('\\');
  }
  if (std::string::npos == pos) {
    return src;
  }
  return std::string(src, 0, pos);
}

bool has_sub_string(const std::string &src, const std::string &sub) {
  return (src.find(sub) != std::string::npos);
}

bool isnumber(const std::string &s) {
  std::regex pattern("-[0-9]+(.[0-9]+)?|[0-9]+(.[0-9]+)?");
  if (std::regex_match(s, pattern)) {
    return true;
  }
  return false;
}

bool is_ip_address(const std::string &s) {
  std::regex pattern("((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){"
                     "3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])");
  if (std::regex_match(s, pattern)) {
    return true;
  }
  return false;
}

bool is_domain_address(const std::string &s) {
  std::regex pattern(
      "([0-9A-Za-z\\-_\\.]+)\\.([0-9a-z]+\\.[a-z]{2,3}(\\.[a-z]{2})?)");
  if (std::regex_match(s, pattern)) {
    return true;
  }
  return false;
}

bool is_valid_host_config(const std::string &s) {
  std::vector<std::string> result;
  split(s, ":", result);
  if (result.size() != 2) {
    return false;
  }
  if (!is_ip_address(result[0])) {
    return false;
  }
  try {
    auto port = std::stoi(result[1]);
    if (0 >= port || port > 65535) {
      return false;
    }
  } catch (...) {
    return false;
  }
  return true;
}

bool get_host_config(const std::string &s, std::string &ip, int &port) {
  std::vector<std::string> result;
  split(s, ":", result);
  if (result.size() != 2) {
    return false;
  }
  if (!is_ip_address(result[0])) {
    return false;
  }
  port = 0;
  try {
    port = std::stoi(result[1]);
    if (0 >= port || port > 65535) {
      return false;
    }
  } catch (...) {
    return false;
  }
  ip = result[0];
  return true;
}

std::string demangle(const std::string &name) {
  constexpr static std::size_t DEMANGLE_BUF_SIZE = 1024;
  char buffer[DEMANGLE_BUF_SIZE] = {0};
#if defined(_MSC_VER)
  DWORD length =
      ::UnDecorateSymbolName(name.c_str(), buffer, sizeof(buffer), 0);
  if (length > 0) {
    return std::string(buffer, length);
  } else {
    return name;
  }
#elif defined(__GNUC__)
  size_t size = sizeof(buffer);
  int status;
  if (abi::__cxa_demangle(name.c_str(), buffer, &size, &status)) {
    return std::string(buffer, size);
  } else {
    return name;
  }
#endif
}

bool is_date_string_fmt1(const std::string &date_string) {
  std::regex pattern("\\d{4}[/]\\d{2}[/]\\d{2} \\d{2}[:]\\d{2}[:]\\d{2}");
  return std::regex_match(date_string, pattern);
}

bool is_date_string_fmt2(const std::string &date_string) {
  std::regex pattern("\\d{4}[-]\\d{2}[-]\\d{2} \\d{2}[:]\\d{2}[:]\\d{2}");
  return std::regex_match(date_string, pattern);
}

std::string readable_size(std::size_t size) {
  constexpr static std::size_t K = 1024;
  constexpr static std::size_t M = K * K;
  if (size > M) {
    return std::to_string(size / M) + "M";
  } else if (size > K) {
    return std::to_string(size / K) + "K";
  } else {
    return std::to_string(size);
  }
}

std::size_t from_readable_size_string(const std::string& size_string) {
  std::string str;
  std::size_t ratio = 1;
  if (endWith(size_string, "K")) {
      str = rtrim(size_string, "K");
      ratio = 1024;
  } else if (endWith(size_string, "M")) {
      str = rtrim(size_string, "M");
      ratio = 1024 * 1024;
  }
  return std::stoull(str) * ratio;
}

bool get_json_root(const std::string &json_string, Json::Value &root,
  std::string& error) {
  Json::CharReaderBuilder builder;
  std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
  const auto *start = json_string.c_str();
  const auto *end = json_string.c_str() + json_string.size();
  return reader->parse(start, end, &root, &error);
}

bool get_json_root_from_file(const std::string &json_file_path, Json::Value &root,
  std::string& error) {
  std::ifstream ifs;
  if (!util::is_path_exists(json_file_path)) {
    return false;
  }
  ifs.open(json_file_path, std::ios::in);
  std::string json_string;
  std::string line;
  while (!ifs.eof()) {
    ifs >> line;
    json_string += line;
  }
  ifs.close();
  return get_json_root(json_string, root, error);
}

} // namespace util
} // namespace kratos
